
/** I N C L U D E S **********************************************************/
#include "p18f2550.h"
#include "GenericTypeDefs.h"
#include "Compiler.h"
#include "HardwareProfile.h"
#include "usb.h"                         
#include "usb_function_cdc.h"
#include "usb_config.h"
#include "usb_device.h"
#include "timer.h"



/** C O N F I G U R A T I O N ************************************************/
// Note: For a complete list of the available config pragmas and their values, 
// see the compiler documentation, and/or click "Help --> Topics..." and then 
// select "PIC18 Config Settings" in the Language Tools section.


//Add the configuration pragmas here for your hardware platform


#pragma config FOSC = HSPLL_HS	// 8 MHz crystal.
#pragma config PLLDIV = 2	 	// Divide by 2 to provide the 96 MHz PLL with 4 MHz input.
#pragma config CPUDIV = OSC1_PLL2 // Divide 96 MHz PLL output by 2 to get 48 MHz system clock.
#pragma config USBDIV = 2	 	// "USB clock source comes from the 96 MHz PLL divided by 2."
#pragma config FCMEN = OFF // "Fail-Safe Clock Monitor disabled."
#pragma config IESO = OFF // "Oscillator Switchover mode disabled."
#pragma config PWRT = OFF // "PWRT disabled."
#pragma config WDT = OFF // "HW Disabled - SW Controlled."
#pragma config MCLRE = ON // "MCLR pin enabled; RE3 input pin disabled."
#pragma config BOR = ON	// Brown Out Reset enabled in hardware. In case of supply voltage drop, BOR resets the device to prevent erratic CPU behavior.
#pragma config BORV = 3	// BOR occurs at 2V (1.2V below the minimum required voltage for the HSPLL oscillator mode).
#pragma config VREGEN = ON	 //USB Voltage Regulator
#pragma config LVP = OFF // Disable low voltage ICSP
//#pragma config ICPRT = OFF // Disable dedicated programming port (44-pin devices)
#pragma config CP0 = OFF // Disable code protection



/** V A R I A B L E S ********************************************************/
#pragma udata



/** P R I V A T E  P R O T O T Y P E S ***************************************/
void USBDeviceTasks(void);
void YourHighPriorityISRCode();
void YourLowPriorityISRCode();
static void InitializeSystem(void);
void USBDeviceTasks(void);
void SetLED(UINT8 ucLEDState);

/** VECTOR REMAPPING ***********************************************/

//On PIC18 devices, addresses 0x00, 0x08, and 0x18 are used for
//the reset, high priority interrupt, and low priority interrupt
//vectors.  However, the current Microchip USB bootloader 
//examples are intended to occupy addresses 
//0x00-0xFFF. Therefore,
//the bootloader code remaps these vectors to new locations
//as indicated below.  This remapping is only necessary if you
//wish to program the hex file generated from this project with
//the USB bootloader.  

#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)
	#define REMAPPED_RESET_VECTOR_ADDRESS			0x1000
	#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS	0x1008
	#define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS	0x1018
#else	
	#define REMAPPED_RESET_VECTOR_ADDRESS			0x00
	#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS	0x08
	#define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS	0x18
#endif
	
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
    extern void _startup (void);        // See c018i.c in your C18 compiler dir
    #pragma code REMAPPED_RESET_VECTOR = REMAPPED_RESET_VECTOR_ADDRESS
    void _reset (void)
    {
        _asm goto _startup _endasm
    }
#endif


#pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS
void Remapped_High_ISR (void)
{
     _asm goto YourHighPriorityISRCode _endasm
}
#pragma code REMAPPED_LOW_INTERRUPT_VECTOR = REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS
void Remapped_Low_ISR (void)
{
     _asm goto YourLowPriorityISRCode _endasm
}

	
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
	//Note: If this project is built while one of the bootloaders has
	//been defined, but then the output hex file is not programmed with
	//the bootloader, addresses 0x08 and 0x18 would end up programmed with 0xFFFF.
	//As a result, if an actual interrupt was enabled and occured, the PC would jump
	//to 0x08 (or 0x18) and would begin executing "0xFFFF" (unprogrammed space).  This
	//executes as nop instructions, but the PC would eventually reach the REMAPPED_RESET_VECTOR_ADDRESS
	//(0x1000 or 0x800, depending upon bootloader), and would execute the "goto _startup".  This
	//would effective reset the application.
	
	//To fix this situation, we should always deliberately place a 
	//"goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS" at address 0x08, and a
	//"goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS" at address 0x18.  When the output
	//hex file of this project is programmed with the bootloader, these sections do not
	//get bootloaded (as they overlap the bootloader space).  If the output hex file is not
	//programmed using the bootloader, then the below goto instructions do get programmed,
	//and the hex file still works like normal.  The below section is only required to fix this
	//scenario.
	#pragma code HIGH_INTERRUPT_VECTOR = 0x08
	void High_ISR (void)
	{
	     _asm goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS _endasm
	}
	#pragma code LOW_INTERRUPT_VECTOR = 0x18
	void Low_ISR (void)
	{
	     _asm goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS _endasm
	}
#endif	//end of "#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_LEGACY_CUSTOM_CLASS_BOOTLOADER)"

#pragma code

//These are your actual interrupt handling routines.
#pragma interrupt YourHighPriorityISRCode
void YourHighPriorityISRCode()
{
	//Check which interrupt flag caused the interrupt.
	//Service the interrupt
	//Clear the interrupt flag
	//Etc.
	//dTest_LED = dLED_ON;
	
	if (INTCONbits.T0IF)
	{
		timer0ISR();
	}	
	else
	{
    #if defined(USB_INTERRUPT)
        USBDeviceTasks();
    #endif
	}
    
    //dTest_LED = dLED_OFF;


}	//This return will be a "retfie fast", since this is in a #pragma interrupt section 
#pragma interruptlow YourLowPriorityISRCode
void YourLowPriorityISRCode()
{
	//Check which interrupt flag caused the interrupt.
	//Service the interrupt
	//Clear the interrupt flag
	//Etc.

}	//This return will be a "retfie", since this is in a #pragma interruptlow section 

	




/** DECLARATIONS ***************************************************/
#pragma code
/******************************************************************************
 * Function:        void main(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Main program entry point.
 *
 * Note:            None
 *****************************************************************************/
void main(void)
{
 UINT8 ucReceivedChar;
 UINT8 ucByteNum;
 UINT8 ucCondInputSend;
 UINT8 ucReceiveState = 0;
 UINT8 ucOutBytePos = 0;
 UINT8 ucNeedToAnswer = 0;
 UINT8 ucaSendThis[3] = {0,0,0};
 UINT8 ucIndex = 0;
 UINT8 ucRxReady = 1;
 UINT8 ucLEDState = 0;
  
 UINT8 ucaBuffer[64];
 
 UINT8 ucaOutput[3];

    InitializeSystem();
    USBDeviceInit();

    while(1)
    {
        //dTest_LED ^= 0b1;
        
        USBDeviceTasks();
        
        if((USBGetDeviceState() < CONFIGURED_STATE) ||
           (USBIsDeviceSuspended() == TRUE))
        {
            //Either the device is not configured or we are suspended
            
            ucLEDState = 0x00;
            
            continue;   //go back to the top of the while loop
        }
        else
        {
            //Keep trying to send data to the PC as required
            CDCTxService();
            
            //If we got "DI" from host we need to send input0-7, input8-15, and chksum byte 
            if(ucRxReady)
            {//We are able to process a new message    
                ucByteNum = getsUSBUSART((char*)ucaBuffer,64);
                if(ucByteNum>0)                             //If we got something
                {
                    ucRxReady = 0;                          //we need to process received chars
                    ucIndex = 0;
                }    
            } 
            else
            {
                ucReceivedChar = ucaBuffer[ucIndex];
                switch(ucReceiveState)
                {
                    case 0:
                    //Wait for 'D'
                        if(ucReceivedChar=='D')
                            ucReceiveState = 1;
                        else if(ucReceivedChar=='!')
                            ucReceiveState = 10;
                    break;
                    
                    case 1:
                    //Wait for 'E'
                        if(ucReceivedChar=='I')
                            ucNeedToAnswer = 1;
                        else if(ucReceivedChar=='!')
                            ucReceiveState = 10;
                        
                        ucReceiveState = 0;
                    break;
                    
                    case 10:
                    //Process outputs
                    
                        ucaOutput[ucOutBytePos]=ucReceivedChar;
                        ucOutBytePos++;
                        
                        if(ucOutBytePos>=3)
                        {//we got enough char
                            ucOutBytePos = 0;
                            ucReceiveState = 0;
                            
                            //Here can handle the outputs
                            //The second byte holds the lower 8 bit outputs
                            if(ucaOutput[1] & dOutSignal1_Mask)
                            {//Signal 1 output controls the Screen ON/OFF LED.
                                ucLEDState |= dScreenON_LED_Mask;
                            }
                            else
                            {
                                ucLEDState &= ~dScreenON_LED_Mask;
                            }
                        }
                    break;
                    
                    default:
                        ucReceiveState = 0;
                    break;
                }//switch
                
                ucIndex++;                                  //Next char
                if(ucIndex >= ucByteNum)
                     ucRxReady = 1;                         //Able to receive next message
                    
            }//if(ucRxReady)                         
            
            if( USBUSARTIsTxTrfReady() && (ucNeedToAnswer == 1) )
            {

                ucCondInputSend = G_ucCondInput;                     // Copy the actual state into the send value
                
                //Handle the long pressed buttons
                if(G_uiaButtonHoldTime[dScreenOnOff]>=dButtonHoldLimit)
                {//Long pressed ScreenOff is equal TurnOff button
                    ucCondInputSend |= dTurnOff_Mask;
                    ucCondInputSend &= ~dScreenOnOff_Mask;
                }
                                    
                ucaSendThis[0] = ~ucCondInputSend;                       //Need to invert. Diatar exept button short to GND
                ucaSendThis[1] = 0;
                ucaSendThis[2] = ~ucCondInputSend;                       //This is the checksum byte
                
                putUSBUSART((char*)ucaSendThis,3);
                
                ucNeedToAnswer = 0;                                     //Answer has been send
            
                G_uiKeepAliveTimer = dKeepAliveInterval;                //Set the keepalive timer
            
                if(ucCondInputSend & dMem1_Mask)
                {
                    ucLEDState &= ~dUSB_LED_Mask;
                    ucLEDState &= ~dMem2_LED_Mask;
                    ucLEDState |= dMem1_LED_Mask;
                }
                else if(ucCondInputSend & dMem2_Mask)
                {
                    ucLEDState &= ~dUSB_LED_Mask;
                    ucLEDState &= ~dMem1_LED_Mask;
                    ucLEDState |= dMem2_LED_Mask;
                }
                else if(ucCondInputSend & dUSB_Mask)    
                {
                    ucLEDState &= ~dMem1_LED_Mask;
                    ucLEDState &= ~dMem2_LED_Mask;
                    ucLEDState |= dUSB_LED_Mask;
                }
            }//if( USBUSARTIsTxTrfReady() &&

                        
            if(G_ucCondInput & dLocal_Mask)
            {//we are in local mode
                
                ucLEDState &= ~dRemote_LED_Mask;
                
                if(G_uiKeepAliveTimer)
                {//communication is active Turn On LEDs
                    ucLEDState &= ~dBlank_LED_Mask;
                }    
                else
                {
                    ucLEDState |= dBlank_LED_Mask;
                }
            }
            else
            {//we are in remote mode
                
                ucLEDState |= dRemote_LED_Mask;
                ucLEDState &= ~dBlank_LED_Mask;
            }//if(G_ucCondInput & dLocal_Mask)     

            SetLED(ucLEDState);                                       //Handle the LEDs
             
        }//else
    }//while(1)
}//end of main()


/********************************************************************
 * Function:        static void InitializeSystem(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        InitializeSystem is a centralize initialization
 *                  routine. All required USB initialization routines
 *                  are called from here.
 *
 *                  User application initialization routine should
 *                  also be called from here.                  
 *
 * Note:            None
 *******************************************************************/
static void InitializeSystem(void)
{
    ADCON1 = 0x0F;			//Set all inputs to digital
    CMCON = 0x07;
    
    dRemote_LED = dLED_OFF;          //Turn off all LEDs
    dScreenON_LED = dLED_OFF;
    dMem1_LED = dLED_OFF;
    dMem2_LED = dLED_OFF;
    dUSB_LED = dLED_OFF;
    dTest_LED = dLED_OFF;            //Turn off Test LED
    
    dRemote_LEDDIR = OUTPUT_PIN;     //Set pin to output
    dScreenON_LEDDIR = OUTPUT_PIN;
    dMem1_LEDDIR = OUTPUT_PIN;
    dMem2_LEDDIR = OUTPUT_PIN;
    dUSB_LEDDIR = OUTPUT_PIN;
    dTest_LEDDIR = OUTPUT_PIN;
    
    dScreenOnOff_PinDIR = INPUT_PIN;    //Set switch pins to input (not neccesary at all, just for fun)
    dReverse_PinDIR = INPUT_PIN;    
    dForward_PinDIR = INPUT_PIN;         
    dTurnOff_PinDIR  = INPUT_PIN;
    dUSB_PinDIR = INPUT_PIN;     
    dMem2_PinDIR = INPUT_PIN;   
    dMem1_PinDIR = INPUT_PIN;   
    dLocal_PinDIR = INPUT_PIN;
    
    initTimer0();
    startTimer0();
    INTCONbits.GIEH = 0b1;              //Enable interrupts

}    




/********************************************************************
 * Function:        void SetLED(UINT8 ucLEDState)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Turn ON or OFF the LEDs
 *                  
 *                  
 *
 *                  
 *                                    
 *
 * Note:            None
 *******************************************************************/
void SetLED(UINT8 ucLEDState)
{
  static UINT8 ucLEDStatePrev = 0xff;
  
    if(ucLEDState != ucLEDStatePrev)
    {//LED state has changed
        if(ucLEDState & dBlank_LED_Mask)
        {//Blank
            dRemote_LED = dLED_OFF;          //Turn off all LEDs
            dScreenON_LED = dLED_OFF;
            dMem1_LED = dLED_OFF;
            dMem2_LED = dLED_OFF;
            dUSB_LED = dLED_OFF;
        }
        else
        {
            if(ucLEDState & dRemote_LED_Mask)
            dRemote_LED= dLED_ON;
            else
            dRemote_LED= dLED_OFF;
            
            if(ucLEDState & dScreenON_LED_Mask)
            dScreenON_LED= dLED_ON;
            else
            dScreenON_LED= dLED_OFF;

            if(ucLEDState & dMem1_LED_Mask)
            dMem1_LED= dLED_ON;
            else
            dMem1_LED= dLED_OFF;

            if(ucLEDState & dMem2_LED_Mask)
            dMem2_LED= dLED_ON;
            else
            dMem2_LED= dLED_OFF;

            if(ucLEDState & dUSB_LED_Mask)
            dUSB_LED= dLED_ON;
            else
            dUSB_LED= dLED_OFF;
        }
      
        ucLEDStatePrev = ucLEDState;
    }

}






/******************************************************************************
 * Function:        void mySetLineCodingHandler(void)
 *
 * PreCondition:    USB_CDC_SET_LINE_CODING_HANDLER is defined
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This function gets called when a SetLineCoding command
 *                  is sent on the bus.  This function will evaluate the request
 *                  and determine if the application should update the baudrate
 *                  or not.
 *
 * Note:            
 *
 *****************************************************************************/
#if defined(USB_CDC_SET_LINE_CODING_HANDLER)
void mySetLineCodingHandler(void)
{
    //If the request is not in a valid range
    if(cdc_notice.GetLineCoding.dwDTERate.Val > 115200)
    {
        //NOTE: There are two ways that an unsupported baud rate could be
        //handled.  The first is just to ignore the request and don't change
        //the values.  That is what is currently implemented in this function.
        //The second possible method is to stall the STATUS stage of the request.
        //STALLing the STATUS stage will cause an exception to be thrown in the 
        //requesting application.  Some programs, like HyperTerminal, handle the
        //exception properly and give a pop-up box indicating that the request
        //settings are not valid.  Any application that does not handle the
        //exception correctly will likely crash when this requiest fails.  For
        //the sake of example the code required to STALL the status stage of the
        //request is provided below.  It has been left out so that this demo
        //does not cause applications without the required exception handling
        //to crash.
        //---------------------------------------
        USBStallEndpoint(0,1);
    }
    else
    {
        //Update the baudrate info in the CDC driver
        CDCSetBaudRate(cdc_notice.GetLineCoding.dwDTERate.Val);
        
        //Update the baudrate of the UART
        #if defined(__18CXX) || defined(__XC8)
        {
            DWORD_VAL dwBaud;
            dwBaud.Val = (DWORD)(GetSystemClock()/4)/line_coding.dwDTERate.Val-1;
            SPBRG = dwBaud.v[0];
            SPBRGH = dwBaud.v[1];
        }    
        #elif defined(__C30__) || defined __XC16__
        {
            DWORD_VAL dwBaud;
            #if defined(__dsPIC33EP512MU810__) || defined (__PIC24EP512GU810__)
            dwBaud.Val = ((GetPeripheralClock()/(unsigned long)(16 * line_coding.dwDTERate.Val)))- 1;
            #else
            dwBaud.Val = (((GetPeripheralClock()/2)+(BRG_DIV2/2*line_coding.dwDTERate.Val))/BRG_DIV2/line_coding.dwDTERate.Val-1);
            #endif
            U2BRG = dwBaud.Val;
        }    
        #elif defined(__C32__)
        {
            U2BRG = ((GetPeripheralClock()+(BRG_DIV2/2*line_coding.dwDTERate.Val))/BRG_DIV2/line_coding.dwDTERate.Val-1);
            //U2MODE = 0;
            U2MODEbits.BRGH = BRGH2;
            //U2STA = 0;
        }    
        #endif
    }
}
#endif


// ******************************************************************************************************
// ************** USB Callback Functions ****************************************************************
// ******************************************************************************************************
// The USB firmware stack will call the callback functions USBCBxxx() in response to certain USB related
// events.  For example, if the host PC is powering down, it will stop sending out Start of Frame (SOF)
// packets to your device.  In response to this, all USB devices are supposed to decrease their power
// consumption from the USB Vbus to <2.5mA* each.  The USB module detects this condition (which according
// to the USB specifications is 3+ms of no bus activity/SOF packets) and then calls the USBCBSuspend()
// function.  You should modify these callback functions to take appropriate actions for each of these
// conditions.  For example, in the USBCBSuspend(), you may wish to add code that will decrease power
// consumption from Vbus to <2.5mA (such as by clock switching, turning off LEDs, putting the
// microcontroller to sleep, etc.).  Then, in the USBCBWakeFromSuspend() function, you may then wish to
// add code that undoes the power saving things done in the USBCBSuspend() function.

// The USBCBSendResume() function is special, in that the USB stack will not automatically call this
// function.  This function is meant to be called from the application firmware instead.  See the
// additional comments near the function.

// Note *: The "usb_20.pdf" specs indicate 500uA or 2.5mA, depending upon device classification. However,
// the USB-IF has officially issued an ECN (engineering change notice) changing this to 2.5mA for all 
// devices.  Make sure to re-download the latest specifications to get all of the newest ECNs.

/******************************************************************************
 * Function:        void USBCBSuspend(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Call back that is invoked when a USB suspend is detected
 *
 * Note:            None
 *****************************************************************************/
void USBCBSuspend(void)
{
	//Example power saving code.  Insert appropriate code here for the desired
	//application behavior.  If the microcontroller will be put to sleep, a
	//process similar to that shown below may be used:
	
	//ConfigureIOPinsForLowPower();
	
    dRemote_LED = dLED_OFF;          //Turn off all LEDs
    dScreenON_LED = dLED_OFF;
    dMem1_LED = dLED_OFF;
    dMem2_LED = dLED_OFF;
    dUSB_LED = dLED_OFF;
    
	//SaveStateOfAllInterruptEnableBits();
	//DisableAllInterruptEnableBits();
	//EnableOnlyTheInterruptsWhichWillBeUsedToWakeTheMicro();	//should enable at least USBActivityIF as a wake source
	//Sleep();
	//RestoreStateOfAllPreviouslySavedInterruptEnableBits();	//Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.
	//RestoreIOPinsToNormal();									//Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.

	//IMPORTANT NOTE: Do not clear the USBActivityIF (ACTVIF) bit here.  This bit is 
	//cleared inside the usb_device.c file.  Clearing USBActivityIF here will cause 
	//things to not work as intended.	
	

 
}



/******************************************************************************
 * Function:        void USBCBWakeFromSuspend(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The host may put USB peripheral devices in low power
 *					suspend mode (by "sending" 3+ms of idle).  Once in suspend
 *					mode, the host may wake the device back up by sending non-
 *					idle state signalling.
 *					
 *					This call back is invoked when a wakeup from USB suspend 
 *					is detected.
 *
 * Note:            None
 *****************************************************************************/
void USBCBWakeFromSuspend(void)
{
	// If clock switching or other power savings measures were taken when
	// executing the USBCBSuspend() function, now would be a good time to
	// switch back to normal full power run mode conditions.  The host allows
	// 10+ milliseconds of wakeup time, after which the device must be 
	// fully back to normal, and capable of receiving and processing USB
	// packets.  In order to do this, the USB module must receive proper
	// clocking (IE: 48MHz clock must be available to SIE for full speed USB
	// operation).  
	// Make sure the selected oscillator settings are consistent with USB 
    // operation before returning from this function.
}

/********************************************************************
 * Function:        void USBCB_SOF_Handler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The USB host sends out a SOF packet to full-speed
 *                  devices every 1 ms. This interrupt may be useful
 *                  for isochronous pipes. End designers should
 *                  implement callback routine as necessary.
 *
 * Note:            None
 *******************************************************************/
void USBCB_SOF_Handler(void)
{
    // No need to clear UIRbits.SOFIF to 0 here.
    // Callback caller is already doing that.
}

/*******************************************************************
 * Function:        void USBCBErrorHandler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The purpose of this callback is mainly for
 *                  debugging during development. Check UEIR to see
 *                  which error causes the interrupt.
 *
 * Note:            None
 *******************************************************************/
void USBCBErrorHandler(void)
{
    // No need to clear UEIR to 0 here.
    // Callback caller is already doing that.

	// Typically, user firmware does not need to do anything special
	// if a USB error occurs.  For example, if the host sends an OUT
	// packet to your device, but the packet gets corrupted (ex:
	// because of a bad connection, or the user unplugs the
	// USB cable during the transmission) this will typically set
	// one or more USB error interrupt flags.  Nothing specific
	// needs to be done however, since the SIE will automatically
	// send a "NAK" packet to the host.  In response to this, the
	// host will normally retry to send the packet again, and no
	// data loss occurs.  The system will typically recover
	// automatically, without the need for application firmware
	// intervention.
	
	// Nevertheless, this callback function is provided, such as
	// for debugging purposes.
}


/*******************************************************************
 * Function:        void USBCBCheckOtherReq(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        When SETUP packets arrive from the host, some
 * 					firmware must process the request and respond
 *					appropriately to fulfill the request.  Some of
 *					the SETUP packets will be for standard
 *					USB "chapter 9" (as in, fulfilling chapter 9 of
 *					the official USB specifications) requests, while
 *					others may be specific to the USB device class
 *					that is being implemented.  For example, a HID
 *					class device needs to be able to respond to
 *					"GET REPORT" type of requests.  This
 *					is not a standard USB chapter 9 request, and 
 *					therefore not handled by usb_device.c.  Instead
 *					this request should be handled by class specific 
 *					firmware, such as that contained in usb_function_hid.c.
 *
 * Note:            None
 *******************************************************************/
void USBCBCheckOtherReq(void)
{
    USBCheckCDCRequest();
}//end


/*******************************************************************
 * Function:        void USBCBStdSetDscHandler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The USBCBStdSetDscHandler() callback function is
 *					called when a SETUP, bRequest: SET_DESCRIPTOR request
 *					arrives.  Typically SET_DESCRIPTOR requests are
 *					not used in most applications, and it is
 *					optional to support this type of request.
 *
 * Note:            None
 *******************************************************************/
void USBCBStdSetDscHandler(void)
{
    // Must claim session ownership if supporting this request
}//end


/*******************************************************************
 * Function:        void USBCBInitEP(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This function is called when the device becomes
 *                  initialized, which occurs after the host sends a
 * 					SET_CONFIGURATION (wValue not = 0) request.  This 
 *					callback function should initialize the endpoints 
 *					for the device's usage according to the current 
 *					configuration.
 *
 * Note:            None
 *******************************************************************/
void USBCBInitEP(void)
{
    //Enable the CDC data endpoints
    CDCInitEP();
}

/********************************************************************
 * Function:        void USBCBSendResume(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The USB specifications allow some types of USB
 * 					peripheral devices to wake up a host PC (such
 *					as if it is in a low power suspend to RAM state).
 *					This can be a very useful feature in some
 *					USB applications, such as an Infrared remote
 *					control	receiver.  If a user presses the "power"
 *					button on a remote control, it is nice that the
 *					IR receiver can detect this signalling, and then
 *					send a USB "command" to the PC to wake up.
 *					
 *					The USBCBSendResume() "callback" function is used
 *					to send this special USB signalling which wakes 
 *					up the PC.  This function may be called by
 *					application firmware to wake up the PC.  This
 *					function will only be able to wake up the host if
 *                  all of the below are true:
 *					
 *					1.  The USB driver used on the host PC supports
 *						the remote wakeup capability.
 *					2.  The USB configuration descriptor indicates
 *						the device is remote wakeup capable in the
 *						bmAttributes field.
 *					3.  The USB host PC is currently sleeping,
 *						and has previously sent your device a SET 
 *						FEATURE setup packet which "armed" the
 *						remote wakeup capability.   
 *
 *                  If the host has not armed the device to perform remote wakeup,
 *                  then this function will return without actually performing a
 *                  remote wakeup sequence.  This is the required behavior, 
 *                  as a USB device that has not been armed to perform remote 
 *                  wakeup must not drive remote wakeup signalling onto the bus;
 *                  doing so will cause USB compliance testing failure.
 *                  
 *					This callback should send a RESUME signal that
 *                  has the period of 1-15ms.
 *
 * Note:            This function does nothing and returns quickly, if the USB
 *                  bus and host are not in a suspended condition, or are 
 *                  otherwise not in a remote wakeup ready state.  Therefore, it
 *                  is safe to optionally call this function regularly, ex: 
 *                  anytime application stimulus occurs, as the function will
 *                  have no effect, until the bus really is in a state ready
 *                  to accept remote wakeup. 
 *
 *                  When this function executes, it may perform clock switching,
 *                  depending upon the application specific code in 
 *                  USBCBWakeFromSuspend().  This is needed, since the USB
 *                  bus will no longer be suspended by the time this function
 *                  returns.  Therefore, the USB module will need to be ready
 *                  to receive traffic from the host.
 *
 *                  The modifiable section in this routine may be changed
 *                  to meet the application needs. Current implementation
 *                  temporary blocks other functions from executing for a
 *                  period of ~3-15 ms depending on the core frequency.
 *
 *                  According to USB 2.0 specification section 7.1.7.7,
 *                  "The remote wakeup device must hold the resume signaling
 *                  for at least 1 ms but for no more than 15 ms."
 *                  The idea here is to use a delay counter loop, using a
 *                  common value that would work over a wide range of core
 *                  frequencies.
 *                  That value selected is 1800. See table below:
 *                  ==========================================================
 *                  Core Freq(MHz)      MIP         RESUME Signal Period (ms)
 *                  ==========================================================
 *                      48              12          1.05
 *                       4              1           12.6
 *                  ==========================================================
 *                  * These timing could be incorrect when using code
 *                    optimization or extended instruction mode,
 *                    or when having other interrupts enabled.
 *                    Make sure to verify using the MPLAB SIM's Stopwatch
 *                    and verify the actual signal on an oscilloscope.
 *******************************************************************/
void USBCBSendResume(void)
{
    static WORD delay_count;
    
    //First verify that the host has armed us to perform remote wakeup.
    //It does this by sending a SET_FEATURE request to enable remote wakeup,
    //usually just before the host goes to standby mode (note: it will only
    //send this SET_FEATURE request if the configuration descriptor declares
    //the device as remote wakeup capable, AND, if the feature is enabled
    //on the host (ex: on Windows based hosts, in the device manager 
    //properties page for the USB device, power management tab, the 
    //"Allow this device to bring the computer out of standby." checkbox 
    //should be checked).
    if(USBGetRemoteWakeupStatus() == TRUE) 
    {
        //Verify that the USB bus is in fact suspended, before we send
        //remote wakeup signalling.
        if(USBIsBusSuspended() == TRUE)
        {
            USBMaskInterrupts();
            
            //Clock switch to settings consistent with normal USB operation.
            USBCBWakeFromSuspend();
            USBSuspendControl = 0; 
            USBBusIsSuspended = FALSE;  //So we don't execute this code again, 
                                        //until a new suspend condition is detected.

            //Section 7.1.7.7 of the USB 2.0 specifications indicates a USB
            //device must continuously see 5ms+ of idle on the bus, before it sends
            //remote wakeup signalling.  One way to be certain that this parameter
            //gets met, is to add a 2ms+ blocking delay here (2ms plus at 
            //least 3ms from bus idle to USBIsBusSuspended() == TRUE, yeilds
            //5ms+ total delay since start of idle).
            delay_count = 3600U;        
            do
            {
                delay_count--;
            }while(delay_count);
            
            //Now drive the resume K-state signalling onto the USB bus.
            USBResumeControl = 1;       // Start RESUME signaling
            delay_count = 1800U;        // Set RESUME line for 1-13 ms
            do
            {
                delay_count--;
            }while(delay_count);
            USBResumeControl = 0;       //Finished driving resume signalling

            USBUnmaskInterrupts();
        }
    }
}


/*******************************************************************
 * Function:        void USBCBEP0DataReceived(void)
 *
 * PreCondition:    ENABLE_EP0_DATA_RECEIVED_CALLBACK must be
 *                  defined already (in usb_config.h)
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This function is called whenever a EP0 data
 *                  packet is received.  This gives the user (and
 *                  thus the various class examples a way to get
 *                  data that is received via the control endpoint.
 *                  This function needs to be used in conjunction
 *                  with the USBCBCheckOtherReq() function since 
 *                  the USBCBCheckOtherReq() function is the apps
 *                  method for getting the initial control transfer
 *                  before the data arrives.
 *
 * Note:            None
 *******************************************************************/
#if defined(ENABLE_EP0_DATA_RECEIVED_CALLBACK)
void USBCBEP0DataReceived(void)
{
}
#endif

/*******************************************************************
 * Function:        BOOL USER_USB_CALLBACK_EVENT_HANDLER(
 *                        USB_EVENT event, void *pdata, WORD size)
 *
 * PreCondition:    None
 *
 * Input:           USB_EVENT event - the type of event
 *                  void *pdata - pointer to the event data
 *                  WORD size - size of the event data
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This function is called from the USB stack to
 *                  notify a user application that a USB event
 *                  occured.  This callback is in interrupt context
 *                  when the USB_INTERRUPT option is selected.
 *
 * Note:            None
 *******************************************************************/
BOOL USER_USB_CALLBACK_EVENT_HANDLER(int event, void *pdata, WORD size)
{
    switch(event)
    {
        case EVENT_TRANSFER:
            //Add application specific callback task or callback function here if desired.
            break;
        case EVENT_SOF:
            USBCB_SOF_Handler();
            break;
        case EVENT_SUSPEND:
            USBCBSuspend();
            break;
        case EVENT_RESUME:
            USBCBWakeFromSuspend();
            break;
        case EVENT_CONFIGURED: 
            USBCBInitEP();
            break;
        case EVENT_SET_DESCRIPTOR:
            USBCBStdSetDscHandler();
            break;
        case EVENT_EP0_REQUEST:
            USBCBCheckOtherReq();
            break;
        case EVENT_BUS_ERROR:
            USBCBErrorHandler();
            break;
        case EVENT_TRANSFER_TERMINATED:
            //Add application specific callback task or callback function here if desired.
            //The EVENT_TRANSFER_TERMINATED event occurs when the host performs a CLEAR
            //FEATURE (endpoint halt) request on an application endpoint which was 
            //previously armed (UOWN was = 1).  Here would be a good place to:
            //1.  Determine which endpoint the transaction that just got terminated was 
            //      on, by checking the handle value in the *pdata.
            //2.  Re-arm the endpoint if desired (typically would be the case for OUT 
            //      endpoints).
            break;
        default:
            break;
    }      
    return TRUE; 
}

/** EOF main.c *************************************************/
